Type MaxMod_Pattern

	Field Title			:String
	Field TrackCount	:Int
	Field Track			:MaxMod_Track[0]
	Field LineCount		:Int
	Field LineText		:String[0]	' quick lookup of text line (for tracker only, fill with a call to UpdateText)

	Function Create:MaxMod_Pattern(Song:MaxMod_Mixer,TrackCount:Int,Lines:Int)
		Local This:MaxMod_Pattern = New MaxMod_Pattern
		This.TrackCount = TrackCount
		This.Track 		= New MaxMod_Track[TrackCount]
		This.Linecount 	= Lines
		This.LineText 	= New String[Lines+1]
		Local n:Int
		For n=0 Until TrackCount
			This.Track[n] = MaxMod_Track.Create(Lines)
		Next
		Return This
	EndFunction

	Function Load:MaxMod_Pattern(f:TStream)
		Local This:MaxMod_Pattern = New MaxMod_Pattern
		Local l:Int 	= ReadShort(f)
		This.Title 		= ReadString(f,l)
		Print "loading pattern ~q"+This.Title+"~q"
		This.LineCount 	= ReadShort(f)
		This.TrackCount = ReadShort(f)
		This.LineText 	= New String[This.LineCount+1]
		This.Track 		= New MaxMod_Track[This.TrackCount]
		For l=0 Until This.TrackCount
			This.Track[l] = MaxMod_Track.Load(f)
		Next 
		Return This
	EndFunction

	Method Save(f:TStream)
		Print "saving pattern ~q"+Title+"~q"
		WriteShort f,Len(Title)
		WriteString f,Title
		WriteShort f,LineCount
		WriteShort f,TrackCount
		Local t:Int
		For t=0 Until TrackCount
			Track[t].Save(f)
		Next
	EndMethod

	Method UpdateText()
		Local line:Int, TXT$, n%
		For line=0 Until LineCount
			Txt$ = Null
			For n=0 Until TrackCount
				TXT$:+Track[n].GetText(Line)+" "
			Next
			LineText$[line] = Txt$			
		Next
	EndMethod

EndType







Type MaxMod_Track

	Field Name		:String
	Field LineCount	:Int
	Field Note		:Int[0]
	Field Inst		:Int[0]
	Field Vol		:Int[0]
	Field FX		:Int[0]
	Field FXV		:Int[0]

	Function Create:MaxMod_Track(Lines:Int)
		Local This:MaxMod_Track
		This			= New MaxMod_Track
		This.LineCount 	= Lines
		This.Note		= New Int[Lines]
		This.Inst		= New Int[Lines]
		This.Vol		= New Int[Lines]
		This.FX			= New Int[Lines]
		This.FXV		= New Int[Lines]
		Return This
	EndFunction

	Function Load:MaxMod_Track(f:TStream)
		Local LineCount:Int = ReadShort(f)
		Local This:MaxMod_Track = MaxMod_Track.Create(LineCount)
		Local l:Int = ReadShort(f)
		This.Name = ReadString(f,l)
		Print "loading track ~q"+This.Name+"~q"
		Local flag:Int
		For l=0 Until LineCount
			flag = ReadByte(f)
			If flag&%00001 This.Note[l] = ReadShort(f)
			If flag&%00010 This.Inst[l] = ReadByte(f)
			If flag&%00100 This.Vol[l]  = ReadByte(f)
			If flag&%01000 This.FX[l]   = ReadByte(f)
			If flag&%10000 This.FXV[l]  = ReadByte(f)
		Next 
		Return This
	EndFunction

	Method Save(f:TStream)
		WriteShort f,LineCount
		WriteShort f,Len(Name)
		WriteString f,Name
		Print "saving track ~q"+Name+"~q"
		Local l:Int, flag:Int
		For l=0 Until LineCount
			flag=0
			If Note[l] 	flag:+%00001
			If Inst[l] 	flag:+%00010
			If Vol[l] 	flag:+%00100
			If FX[l] 	flag:+%01000
			If FXV[l] 	flag:+%10000
			WriteByte f,flag
			If Note[l] WriteShort f,Note[l]
			If Inst[l] WriteByte f,Inst[l]
			If Vol[l]	WriteByte f,Vol[l]
			If FX[l]	WriteByte f,FX[l]
			If FXV[l]  WriteByte f,FXV[l]
		Next
	EndMethod

	Method IsIdentical:Int(with:MaxMod_Track)
		If LineCount<>with.LineCount Return False
		Local Line:Int
		For Line=0 Until LineCount
			If Note[Line] <> with.Note[Line] Return False
			If Inst[Line] <> with.Inst[Line] Return False
			If Vol[Line]  <> with.Vol[Line]  Return False
			If FX[Line]   <> with.FX[Line]   Return False
			If FXV[Line]  <> with.FXV[Line]  Return False
		Next
		Return True
	EndMethod

	Method GetBVal:Int(line:Int)
		If Note[line] And Inst[line] And Vol[line] And FX[line] And FXV[line] Then Return 0
		Local bval:Int=%10000000
		If Note[line] Then bval:+%00000001
		If Inst[line] Then bval:+%00000010
		If Vol [line] Then bval:+%00000100
		If FX  [line] Then bval:+%00001000
		If FXV [line] Then bval:+%00010000
		Return bval
	EndMethod

	Global NoteTag$[] =	 ["C 0","C#0","D 0","D#0","E 0","F 0","F#0","G 0","G#0","A 0","A#0","B 0" ..
						 ,"C 1","C#1","D 1","D#1","E 1","F 1","F#1","G 1","G#1","A 1","A#1","B 1" ..
						 ,"C 2","C#2","D 2","D#2","E 2","F 2","F#2","G 2","G#2","A 2","A#2","B 2" ..
						 ,"C 3","C#3","D 3","D#3","E 3","F 3","F#3","G 3","G#3","A 3","A#3","B 3" ..
						 ,"C 4","C#4","D 4","D#4","E 4","F 4","F#4","G 4","G#4","A 4","A#4","B 4" ..
						 ,"C 5","C#5","D 5","D#5","E 5","F 5","F#5","G 5","G#5","A 5","A#5","B 5" ..
						 ,"C 6","C#6","D 6","D#6","E 6","F 6","F#6","G 6","G#6","A 6","A#6","B 6" ..
						 ,"C 7","C#7","D 7","D#7","E 7","F 7","F#7","G 7","G#7","A 7","A#7","B 7" ..
						 ,"C 8","C#8","D 8","D#8","E 8","F 8","F#8","G 8","G#8","A 8","A#8","B 8" ]

	Function GetNoteTag$(n:Int)
		If n>0 And n<NoteTag.Length
			Return NoteTag$[n]
		ElseIf n<0 
			Return "<<<"	' out of range
		Else
			Return ">>>"	' out of range
		EndIf
	EndFunction


	Method GetText$(Line%)
		Local TXT$
		If NOTE[Line]>0 And NOTE[Line]<96 ; TXT$ = GetNoteTag$(NOTE[Line])+" " ; ElseIf NOTE[Line]=96 ; TXT$ = " |  " ; Else ; TXT$	  = "--- "
        If Inst[Line]>0 ; TXT$:+Right$(Hex(Inst[Line]),2)+" " ; Else ; TXT$:+ "   "
        If Vol [Line]>0 ; TXT$:+Right$(Hex(Vol[Line]),2)+" " ; Else ; TXT$:+ "   "
		If FX  [Line]=0 And FXV[Line]=0
			TXT$:+"   " 
		Else
			If FX[Line]=<$F
				TXT$:+ Right$(Hex(FX[Line]),1) 
			Else
				TXT$:+ Chr$(70+(FX[Line]-$F))
			EndIf
			TXT$:+ Right$(Hex(FXV[Line]),2)
		EndIf
		Return TXT$
	EndMethod

EndType
